import { useRef, useState, useCallback } from 'react';
export interface OptionsType<T> {
  max: number;
  autoPush: boolean;
  onRemove: (state: T, queue: Array<T>, options: OptionsType<T>) => void;
  onAdd: (state: T, queue: Array<T>, options: OptionsType<T>) => void;
  onUpdate: (
    prevState: T,
    state: T,
    queue: Array<T>,
    options: OptionsType<T>
  ) => void;
}
type PartialOptionsType<T> = Partial<OptionsType<T>>;
export interface StateType extends Object {
  id: string;
}
export default function useStateStack<T extends StateType>(
  state: T,
  userOptions: PartialOptionsType<T> = {}
) {
  const defaultOptions: OptionsType<T> = {
    max: 100,
    autoPush: true,
    onAdd: (state) => console.log('useStateStack add', state),
    onRemove: (state) => console.log('useStateStack remove', state),
    onUpdate: (prevState, state) => console.log('useStateStack update', state),
  };

  const queue = useRef<T[]>([]);
  const [options, setOptions] = useState(() =>
    Object.assign({}, defaultOptions, userOptions)
  );
  const push = useCallback(
    (state: T) => {
      const targetIndex = queue.current.findIndex((x) => x.id === state.id);
      const target = queue.current[targetIndex];
      const top: T = queue.current[0];
      const data = {
        options,
        queue,
        top,
      };
      if (target != null) {
        options.onUpdate(target, state, queue.current, options);
        queue.current.splice(targetIndex, 1);
      }
      queue.current.unshift(state);
      if (queue.current.length > options.max) {
        queue.current.splice(queue.current.length - 1, 1);
        options.onRemove(top, queue.current, options);
      }
      options.onAdd(state, queue.current, options);
      return data;
    },
    [options]
  );
  const top = useCallback(() => {
    return queue.current[0];
  }, []);
  const prev = useCallback(() => {
    return queue.current[1];
  }, []);
  const pop = useCallback(
    (id: string) => {
      const index = queue.current.findIndex((x) => x.id === id);
      if (index !== -1) {
        const target = queue.current.splice(index, 1)[0];
        options.onRemove(target, queue.current, options);
      }
    },
    [options]
  );
  if (options.autoPush) {
    push(state);
  }
  return {
    push,
    pop,
    top,
    prev,
    setOptions,
  };
}
